`include "C:\Users\lenovo\Desktop\Files\Linear_RISCV\LR_ver_0\src\include\include.vh"
module 	exu(
    input	   [63:0]		imm_exu_i,
    input	   [63:0]	     PC_exu_i,

    input	   [63:0]	  Rs1_exu_i,
    input	   [63:0]	  Rs2_exu_i, 

    input	   [63:0]		 Rd_lsu,   
    input	   [63:0]		 Rd_wbu,

    input      [1: 0]      forwar_1,
    input      [1: 0]      forwar_2,

    input      [4: 0]  ctrl_bus_exu_i,

    input      [5: 0]      uop_code,
    input      [3: 0]      exe_type,

    output reg [63:0]      exu_out,
    output reg [63:0] 	    PC_exu,
    output     [63:0]   Store_data,

    output	   [2: 0] Load_sel_exu,
    output     [2: 0] ctrl_bus_exu_o,
    
    output reg        Branch_taken
);
    assign ctrl_bus_exu_o = ctrl_bus_exu_i[2:0]; 
    forward inst_forward(
          Rs1_exu_i,
          Rs2_exu_i,

          Rd_lsu,   
          Rd_wbu,

          forwar_1,
          forwar_2,

          Rs1_exu,
          Rs2_exu
    ); //Forward Procrss

    wire  [63:0]    LUI_o;
    lui_u lui_inst(
          uop_code,
           imm_exu_i,
             LUI_o
    );//lui process

    wire  [63:0]    AUIPC_o;
    auipc_u Auipc_inst(
        uop_code,
         imm_exu_i,
          PC_exu_i,
         AUIPC_o
    );//auipc process

    wire  [63:0]    JAL_o;   
    wire  [63:0]    JAL_pc;
    jal_u Jal_inst(
          uop_code,
           imm_exu_i,
            PC_exu_i,    
             JAL_o,
            JAL_pc
    );//jal process 


    wire  [63:0]    JALR_pc;
    wire  [63:0]    JALR_o;
    jalr_u Jalr_inst(
          uop_code,
           imm_exu_i,
           Rs1_exu,
            PC_exu_i,    
            JALR_o,
           JALR_pc
    ); //jalr process


    wire  [63:0]    PC_b;
    wire       Branch_en;
    branch_u Branch_inst(
             uop_code,
        	  Rs1_exu,
        	  Rs2_exu,
        	  imm_exu_i,
        	   PC_exu_i,
            Branch_en,	   
        	     PC_b
    );//Branch process

    wire  [63:0]    LOAD_addr;
    load_u Load_inst(
             uop_code,
              imm_exu_i,
              Rs1_exu,
         Load_sel_exu,
            LOAD_addr
    );//Load process

    wire  [63:0]    DMEM_addr;
    store_u Store_inst(
               uop_code,
                Rs1_exu,
                Rs2_exu,
    	        imm_exu_i,
              DMEM_addr,
             Store_data
    );//Store process

    wire  [63:0]    Reg_out;
    common_u Common_inst(
               uop_code,
        		 Rs1_exu,
        		 Rs2_exu,
        		 imm_exu_i,
                 Reg_out
    );//Common computing

    wire  [63:0]    divr_out;
    div_rem_u Div_Rem_inst(
                uop_code,
        		 Rs1_exu,
        		 Rs2_exu,
                 divr_out
    );//div computing

    wire  [63:0]    Mul_out;
    mul_u       Mul_inst(
                uop_code,
        		 Rs1_exu,
        		 Rs2_exu,
                 Mul_out
    );//rem computing

    always@(*) begin //nomal ALU_out
        case(exe_type)
            `EXE_TYPE_AUIPC:    exu_out=AUIPC_o;
            `EXE_TYPE_COMMON:   exu_out=Reg_out;
            `EXE_TYPE_LOAD:     exu_out=LOAD_addr;
            `EXE_TYPE_STORE:    exu_out=DMEM_addr;
            `EXE_TYPE_DIV:      exu_out=divr_out;
            `EXE_TYPE_JAL:      exu_out=JAL_o;
            `EXE_TYPE_JALR:     exu_out=JALR_o;
            `EXE_TYPE_LUI:      exu_out=LUI_o;
            `EXE_TYPE_MUL:      exu_out=Mul_out;
            default:            exu_out=`REGD_ZERO;
        endcase
    end

    always@(*) begin
        case(exe_type)
            `EXE_TYPE_BRANCH:   PC_exu=PC_b;
            `EXE_TYPE_JAL:      PC_exu=JAL_pc;
            `EXE_TYPE_JALR:     PC_exu=JALR_pc;
            default:            PC_exu=`REGD_ZERO;
        endcase
    end

    always@(*) begin
        case(exe_type)
            `EXE_TYPE_BRANCH:   Branch_taken=Branch_en&ctrl_bus_exu_i[4];
            `EXE_TYPE_JAL:      Branch_taken=ctrl_bus_exu_i[3];
            `EXE_TYPE_JALR:     Branch_taken=ctrl_bus_exu_i[3];
            default:            Branch_taken=`LOGI_FALSE;
        endcase
    end

    

endmodule



module 	branch_u(
    input       [5: 0]   uop_code,
    input	    [63:0]	  Rs1_exu,
    input	    [63:0]	  Rs2_exu,
    input	    [63:0]	  imm_exu_i,
    input	    [63:0]	   PC_exu_i,
    output	reg         Branch_en,	   
    output	    [63:0]	     PC_b
); assign PC_b = PC_exu_i + imm_exu_i;
always@(*) begin
    case(uop_code)
        `UOP_CODE_BEQ:
            Branch_en = (Rs1_exu == Rs2_exu) ;
        `UOP_CODE_BNE:
            Branch_en = (Rs1_exu != Rs2_exu) ;
        `UOP_CODE_BLT:
            Branch_en = (Rs1_exu <  Rs2_exu);
        `UOP_CODE_BLTU:
            Branch_en = ($unsigned(Rs1_exu)<$unsigned(Rs2_exu)) ;
        `UOP_CODE_BGE:
            Branch_en = (Rs1_exu >=  Rs2_exu);
        `UOP_CODE_BGEU:
            Branch_en = ($unsigned(Rs1_exu)>=$unsigned(Rs2_exu));
        default: 
            Branch_en = `LOGI_FALSE;
    endcase
end
endmodule

module 	load_u(
    input       [5: 0]   uop_code,
    input	    [63:0]	  imm_exu_i,
    input	    [63:0]	  Rs1_exu,
    output	reg [2: 0]   Load_sel,
    output	    [63:0]  LOAD_addr
);  assign     LOAD_addr = Rs1_exu + imm_exu_i;
always@(*) begin
    case(uop_code)
        `UOP_CODE_LB:
            Load_sel=`LB;
        `UOP_CODE_LBU:
            Load_sel=`LBU;
        `UOP_CODE_LH:
            Load_sel=`LH;
        `UOP_CODE_LHU:
            Load_sel=`LHU;
        `UOP_CODE_LW:
            Load_sel=`LW;
        `UOP_CODE_LWU:
            Load_sel=`LWU;
        `UOP_CODE_LD:
            Load_sel=`LD;
        default: 
            Load_sel=`LOAD_DIS;
    endcase
end
endmodule

module 	store_u(
    input      [5: 0]       uop_code,
    input	   [63:0]		 Rs1_exu,
    input	   [63:0]		 Rs2_exu,
    input	   [63:0]		 imm_exu_i,
    output	   [63:0]      DMEM_addr,
    output reg [63:0]     Store_data
);  assign     DMEM_addr = Rs1_exu + imm_exu_i;
    always@(*) begin
        case(uop_code)
            `UOP_CODE_SB:
                Store_data=Rs2_exu[7: 0];
            `UOP_CODE_SH:
                Store_data=Rs2_exu[15:0];
            `UOP_CODE_SW:
                Store_data=Rs2_exu[31:0];
            `UOP_CODE_SD:
                Store_data=Rs2_exu;
            default: 
                Store_data=`REGD_ZERO;
        endcase
    end

endmodule

module 	common_u(
    input      [5: 0]       uop_code,
    input	   [63:0]		 Rs1_exu,
    input	   [63:0]		 Rs2_exu,
    input	   [63:0]		 imm_exu_i,
    output reg [63:0]        Reg_out
);         reg [31:0]      shift_reg;
           reg [31:0]        sub_reg;
always@(*) begin
    case(uop_code)
        `UOP_CODE_ADD:    Reg_out=Rs1_exu+Rs2_exu;
        `UOP_CODE_ADDI:   Reg_out=Rs1_exu+imm_exu_i;
        `UOP_CODE_ADDW:   Reg_out={32'b0,Rs1_exu[31:0]+Rs2_exu[31:0]};           
        `UOP_CODE_ADDIW:  Reg_out={32'b0,Rs1_exu[31:0]+imm_exu_i[31:0]}; 
        `UOP_CODE_AND:    Reg_out=Rs1_exu&Rs2_exu;
        `UOP_CODE_ANDI:   Reg_out=Rs1_exu&imm_exu_i;
        `UOP_CODE_OR:     Reg_out=Rs1_exu|Rs2_exu;
        `UOP_CODE_ORI:    Reg_out=Rs1_exu|imm_exu_i;
        `UOP_CODE_SLL:    Reg_out=Rs1_exu<<Rs2_exu[5:0];
        `UOP_CODE_SLLI:   Reg_out=Rs1_exu<<imm_exu_i[5:0];
        `UOP_CODE_SLLW:
            begin
                shift_reg = Rs1_exu[31:0]<<Rs2_exu[4:0];
                Reg_out = Rs2_exu[5]?`REGD_ZERO:{{32{shift_reg[31]}},shift_reg[31:0]};
            end   
        `UOP_CODE_SLLWI:
            begin
                shift_reg = Rs1_exu[31:0]<<imm_exu_i[4:0];
                Reg_out = imm_exu_i[5]?`REGD_ZERO:{{32{shift_reg[31]}},shift_reg[31:0]};
            end   
        `UOP_CODE_SLT:    Reg_out=(Rs1_exu<=Rs2_exu);
        `UOP_CODE_SLTU:   Reg_out=($unsigned(Rs1_exu)<=$unsigned(Rs2_exu));
        `UOP_CODE_SLTI:   Reg_out=(Rs1_exu<=imm_exu_i);
        `UOP_CODE_SLTIU:  Reg_out=($unsigned(Rs1_exu)<=$unsigned(imm_exu_i));
        `UOP_CODE_SRL:    Reg_out=Rs1_exu>>Rs2_exu[5:0];
        `UOP_CODE_SRLI:   Reg_out=Rs1_exu>>imm_exu_i[5:0];
        `UOP_CODE_SRLW:
            begin
                shift_reg = Rs1_exu[31:0]>>Rs2_exu[4:0];
                Reg_out = Rs2_exu[5]?`REGD_ZERO:{{32{shift_reg[31]}},shift_reg[31:0]};
            end   
        `UOP_CODE_SRLWI:
            begin
                shift_reg = Rs1_exu[31:0]>>imm_exu_i[4:0];
                Reg_out = imm_exu_i[5]?`REGD_ZERO:{{32{shift_reg[31]}},shift_reg[31:0]};
            end   
        `UOP_CODE_SRA:    Reg_out=Rs1_exu>>>Rs2_exu[5:0];
        `UOP_CODE_SRAI:   Reg_out=Rs1_exu>>>imm_exu_i[5:0];
        `UOP_CODE_SRAW:
            begin
                shift_reg = Rs1_exu[31:0]>>>Rs2_exu[4:0];
                Reg_out = Rs2_exu[5]?`REGD_ZERO:{{32{shift_reg[31]}},shift_reg[31:0]};
            end   
        `UOP_CODE_SRAWI:
            begin
                shift_reg = Rs1_exu[31:0]>>imm_exu_i[4:0];
                Reg_out = imm_exu_i[5]?`REGD_ZERO:{{32{shift_reg[31]}},shift_reg[31:0]};
            end   
        `UOP_CODE_SUB:    Reg_out=Rs1_exu-Rs2_exu;
        `UOP_CODE_SUBW:
            begin
                sub_reg = Rs1_exu[31:0]-Rs2_exu[31:0];
                Reg_out = {{32{sub_reg[31]}},sub_reg};
            end
        `UOP_CODE_XOR:    Reg_out=Rs1_exu^Rs2_exu;
        `UOP_CODE_XORI:   Reg_out=Rs1_exu^imm_exu_i;        
        default:          Reg_out=`REGD_ZERO;
    endcase
end
endmodule

module 	lui_u(
    input      [5: 0]       uop_code,
    input	   [63:0]	     imm_exu_i,
    output	   [63:0]          LUI_o
);    assign LUI_o = (uop_code == `UOP_CODE_LUI)?imm_exu_i:`REGD_ZERO;
endmodule

module 	auipc_u(
    input      [5: 0]       uop_code,
    input	   [63:0]	     imm_exu_i,
    input	   [63:0]	      PC_exu_i,
    output	   [63:0]        AUIPC_o
);    assign AUIPC_o = (uop_code == `UOP_CODE_LUI)?(imm_exu_i+PC_exu_i):`REGD_ZERO;
endmodule

module 	jal_u(
    input      [5: 0]       uop_code,
    input	   [63:0]	     imm_exu_i,
    input	   [63:0]	      PC_exu_i,    
    output	   [63:0]          JAL_o,
    output	   [63:0]         JAL_pc
);  assign  JAL_o = (uop_code == `UOP_CODE_JAL)?(PC_exu_i +   64'd4):`REGD_ZERO;
    assign  JAL_pc= (uop_code == `UOP_CODE_JAL)?(PC_exu_i + imm_exu_i):`REGD_ZERO;
endmodule

module 	jalr_u(
    input      [5: 0]       uop_code,
    input	   [63:0]	     imm_exu_i,
    input	   [63:0]		 Rs1_exu,
    input	   [63:0]	      PC_exu_i,    
    output	   [63:0]         JALR_o,
    output	   [63:0]        JALR_pc
);  assign  JALR_o = (uop_code == `UOP_CODE_JALR)?(PC_exu_i +   64'd4):`REGD_ZERO;
    assign  JALR_pc= (uop_code == `UOP_CODE_JALR)?(Rs1_exu+ imm_exu_i):`REGD_ZERO;
endmodule

module 	mul_u(
    input      [5: 0]       uop_code,
    input	   [63:0]		 Rs1_exu,
    input	   [63:0]		 Rs2_exu,
    output reg [63:0]        Mul_out
);         reg [127:0]        MulU_result;
           reg [127:0]        MulS_result;
           reg [127:0]       MulSU_result;
           reg [63: 0]        MulW_result;
               
        (*use_dsp48="yes"*)
    always@(*)begin
        MulU_result =$unsigned(Rs1_exu)*$unsigned(Rs2_exu);
        MulS_result =Rs1_exu*Rs2_exu;
        MulSU_result=Rs1_exu*$unsigned(Rs2_exu);
        MulW_result=Rs1_exu[31:0]*Rs2_exu[31:0];
    end
    always@(*) begin
        case(uop_code)
            `UOP_CODE_MUL:       Mul_out=MulS_result[63:0];
            `UOP_CODE_MULH:      Mul_out=MulS_result[127:64];
            `UOP_CODE_MULHU:     Mul_out=MulU_result[127:64];
            `UOP_CODE_MULHSU:    Mul_out=MulSU_result[127:64];
            `UOP_CODE_MULW:      Mul_out=MulW_result;       
            default:             Mul_out=`REGD_ZERO;
        endcase
    end
endmodule

module div_rem_u(
    input      [5: 0]       uop_code,
    input	   [63:0]		 Rs1_exu,
    input	   [63:0]		 Rs2_exu,
    output reg [63:0]        divr_out
);         reg [63:0]        div_out;
           reg [63:0]        divu_out;
           reg [63:0]        rem_out;
           reg [63:0]        remu_out;
           reg [31:0]        divw_out;
           reg [31:0]        divuw_out;
           reg [31:0]        remw_out;
           reg [31:0]        remuw_out;
        (*use_dsp48="yes"*)
    always@(*) begin
        div_out  = Rs1_exu/Rs2_exu;
        divu_out = Rs1_exu/$unsigned(Rs2_exu);
        divw_out = Rs1_exu[31:0]/Rs2_exu[31:0];
        divuw_out= Rs1_exu[31:0]/$unsigned(Rs2_exu[31:0]);
        rem_out  = Rs1_exu%Rs2_exu;
        remu_out = Rs1_exu%$unsigned(Rs2_exu);
        remw_out = Rs1_exu[31:0]%Rs2_exu[31:0];
        remuw_out= Rs1_exu[31:0]%$unsigned(Rs2_exu[31:0]);
    end    
    always@(*) begin
        case(uop_code)
            `UOP_CODE_DIV:           divr_out = div_out ;
            `UOP_CODE_DIVU:          divr_out = divu_out ;
            `UOP_CODE_DIVW:          divr_out = divw_out ;
            `UOP_CODE_DIVUW:         divr_out = divuw_out ;
            `UOP_CODE_REM:           divr_out = rem_out ;
            `UOP_CODE_REMU:          divr_out = remu_out ;
            `UOP_CODE_REMW:          divr_out = remw_out ;
            `UOP_CODE_REMUW:         divr_out = remuw_out;
            default:                 divr_out = `REGD_ZERO;
        endcase
    end

endmodule
